#![cfg_attr(not(feature = "std"), no_std)]

// Re-export pallet items so that they can be accessed from the crate namespace.
pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use frame_support::{
        dispatch::DispatchResultWithPostInfo,
        ensure,
        pallet_prelude::*,
        traits::{Hooks, IsType},
        Blake2_128Concat,
    };
    use frame_system::{ensure_signed, pallet_prelude::*};
    use sp_std::vec::Vec;
    #[pallet::config]
    pub trait Config: frame_system::Config {
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
    }

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    // The pallet's runtime storage items.
    // https://substrate.dev/docs/en/knowledgebase/runtime/storage
    #[pallet::storage]
    #[pallet::getter(fn proofs)]
    // Learn more about declaring storage items:
    // https://substrate.dev/docs/en/knowledgebase/runtime/storage#declaring-storage-items
    pub(super) type Proofs<T: Config> =
        StorageMap<_, Blake2_128Concat, Vec<u8>, (T::AccountId, T::BlockNumber), ValueQuery>;

    // Pallets use events to inform users when important changes are made.
    // https://substrate.dev/docs/en/knowledgebase/runtime/events
    #[pallet::event]
    #[pallet::metadata(T::AccountId = "AccountId")]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        /// Event documentation should end with an array that provides descriptive names for event
        ClaimRevoked(Vec<u8>, T::AccountId),
        ClaimCreate(Vec<u8>, T::AccountId),
    }

    #[pallet::error]
    pub enum Error<T> {
        /// Error names should be descriptive.
        NotProofOwner,
        /// Errors should have helpful documentation associated with them.
        NoSuchProof,
        ProofAlreadyClaimed,
    }

    #[pallet::hooks]
    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// An example dispatchable that takes a singles value as a parameter, writes the value to
        /// storage and emits an event. This function must be dispatched by a signed extrinsic.
        #[pallet::weight(10_000)]
        pub fn creat_claim(
            origin: OriginFor<T>,
            proof_hash: Vec<u8>,
        ) -> DispatchResultWithPostInfo {
            // Check that the extrinsic was signed and get the signer.
            // This function will return an error if the extrinsic is not signed.
            // https://substrate.dev/docs/en/knowledgebase/runtime/origin
            let sender = ensure_signed(origin)?;
            ensure!(
                !Proofs::<T>::contains_key(&proof_hash),
                Error::<T>::ProofAlreadyClaimed
            );
            // Update storage.
            <Proofs<T>>::insert(
                &proof_hash,
                (&sender, frame_system::Pallet::<T>::block_number()),
            );

            // Emit an event.
            Self::deposit_event(Event::<T>::ClaimCreate(proof_hash, sender));
            // Return a successful DispatchResultWithPostInfo
            Ok(().into())
        }

        /// An example dispatchable that may throw a custom error.
        #[pallet::weight(10_000)]
        pub fn revoke_claim(origin: OriginFor<T>, proof: Vec<u8>) -> DispatchResultWithPostInfo {
            let sender = ensure_signed(origin)?;
            // Verify that the specified proof has been claimed.
            ensure!(Proofs::<T>::contains_key(&proof), Error::<T>::NoSuchProof);
            //get the owner
            let (owner, _) = Proofs::<T>::get(&proof);
            ensure!(owner == sender, Error::<T>::NotProofOwner);
            Proofs::<T>::remove(&proof);
            Self::deposit_event(Event::<T>::ClaimRevoked(proof, sender));
            Ok(().into())
        }

        #[pallet::weight(10_000)]
        pub fn trasfer(
            origin: OriginFor<T>,
            proof: Vec<u8>,
            to: T::AccountId,
        ) -> DispatchResultWithPostInfo {
            let caller = ensure_signed(origin)?;

			Self::ensure_owner(&proof,&caller)?;
			// transfer owner
			<Proofs<T>>::insert(
                &proof,
                (&to, frame_system::Pallet::<T>::block_number()),
            );
            Ok(().into())
        }
    }

	impl<T:Config> Pallet<T>{
		fn ensure_owner(proof:&Vec<u8>,caller:&T::AccountId)->DispatchResultWithPostInfo{
    	//Verify that the specified proof has been claimed.
    	ensure!(Proofs::<T>::contains_key(&proof),Error::<T>::NoSuchProof);
    	//get the owner
    	let (owner,_) = Proofs::<T>::get(&proof);
    	ensure!(owner==*caller,Error::<T>::NotProofOwner);
    	Ok(().into())
    }
	}
}
